home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Think Class Libraries / CVoice 1.2 / CVoice.cp next >
Encoding:
Text File  |  1994-11-30  |  18.0 KB  |  738 lines  |  [TEXT/KAHL]

  1. /******************************************************************************
  2.  CVoice.cp        Version1.2
  3.  
  4.  Written by Brian Stern  <Jaeger@fquest.com>
  5.  
  6.  Written to work with TCL 2.x
  7.  
  8.      A voice class that implements a speech manager voice.  To use this class
  9.      most simply, call the constructor with a known voice.  To speak call SpeakTheText.
  10.      
  11.      Str255    someText = {"\pHello from planet Zircon."};
  12.      CVoice *theVoice = new CVoice( "\pBoris");
  13.      theVoice->SpeakTheText( someText, Length(someText) );
  14.  
  15.     This class is Copyright © 1994, Brian Stern.  It may be used freely in 
  16.     any projects.  If you do anything interesting with it send me email.
  17.  
  18. ******************************************************************************/
  19.  
  20. #include "CVoice.h"
  21. #include "GestaltEqu.h"
  22.  
  23. extern    CBartender        *gBartender;    /* The menu handling object */
  24.  
  25. voicetype    CVoice::cHasSpeechMan = knotInited;
  26. short        CVoice::cVoiceMenuID = -1;        //Initialize to invalid value
  27.  
  28. /******************************************************************************
  29.  CVoice
  30.  
  31.      Default constructor.
  32. ******************************************************************************/
  33.  
  34. CVoice::CVoice(void)
  35. {
  36.     InitVoice();
  37.  
  38. }
  39.  
  40. /******************************************************************************
  41.  CVoice
  42.  
  43.      Alternate constructor.
  44. ******************************************************************************/
  45.  
  46. CVoice::CVoice( const Str63 theVoice )
  47. {
  48.     InitVoice();
  49.     if ( cHasSpeechMan == kHasSpeechMan )
  50.         SetVoice( theVoice );
  51.  
  52. }
  53.  
  54. /******************************************************************************
  55.  ~CVoice
  56.  
  57.  
  58. ******************************************************************************/
  59.  
  60. CVoice::~CVoice( void )
  61. {
  62.     if ( fGoodChannel )
  63.         (void) DisposeSpeechChannel( ftheChannel );
  64.  
  65.     ForgetPtr( fTextPtr );
  66.  
  67. }
  68.  
  69. /******************************************************************************
  70.  InitVoice
  71.  
  72.     Private initializer function.
  73. ******************************************************************************/
  74.  
  75. void
  76. CVoice::InitVoice( void )
  77. {
  78.     OSErr            err;
  79.     long            result;
  80.  
  81.     //Is the Speech Manager present?
  82.     if( cHasSpeechMan == knotInited )
  83.     {
  84.         err = Gestalt( gestaltSpeechAttr, &result );
  85.         if ( ( err != noErr ) || !( result & ( 1 << gestaltSpeechMgrPresent ) ) )
  86.             cHasSpeechMan = kNoHasSpeechMan;
  87.         else
  88.             cHasSpeechMan = kHasSpeechMan;
  89.     }
  90.  
  91.     fGoodChannel = FALSE;
  92.  
  93.     if ( cHasSpeechMan == kHasSpeechMan )
  94.     {
  95.         //Allocate a pointer to hold the string to be spoken
  96.         fTextPtr = NewPtr( kPtrLen );
  97.         FailNIL( fTextPtr );
  98.         fTextPtr[0] = 0;
  99.         fTextPtrSize = kPtrLen;
  100.         fInputFormat = kTextInput;    //All voices default to this
  101.     }
  102.  
  103. }
  104.  
  105. /******************************************************************************
  106.  SetVoice
  107.  
  108.     Set the current voice for this object.  This method must be called once
  109.     before any speaking can take place.  theVoice is a PString.  If a match
  110.     isn't found then the system default voice is chosen as a default voice.  
  111.     Returns speech manager errors or kNoSpManErr if Speech Man isn't present.
  112.  
  113. ******************************************************************************/
  114.  
  115. OSErr 
  116. CVoice::SetVoice( const Str63 theVoice )
  117. {
  118.     OSErr                err = kNoSpManErr;
  119.     long                i;
  120.     short                NumVoices = 0;
  121.     VoiceDescription    VD;
  122.     VoiceSpec            aVSpec;
  123.     SpeechChannel        theChannel;
  124.  
  125.     if ( cHasSpeechMan == kHasSpeechMan )
  126.     {
  127.         (void) CountVoices( &NumVoices );
  128.     
  129.         for ( i = 1; i <= NumVoices; i++ )
  130.         {
  131.             err = GetIndVoice( i, &aVSpec );
  132.             err = GetVoiceDescription( &aVSpec, &VD, sizeof(VD) );
  133.                 if ( EqualString( theVoice, VD.name, FALSE, FALSE ) )
  134.                     break;
  135.         }
  136.     
  137.         if ( i > NumVoices )    /*  If specified voice isn't found use the default system voice */
  138.         {
  139.             err = GetVoiceDescription( NULL, &VD, sizeof(VD) );// Get the VSpec of the default voice
  140.             aVSpec = VD.voice;
  141.         }
  142.  
  143.         ftheSpec = aVSpec;
  144.         
  145.         // Dispose of the old channel if it exists
  146.         if ( fGoodChannel )
  147.         {
  148.             (void) DisposeSpeechChannel( ftheChannel );
  149.             fCurrVoiceItem = 0;
  150.         }
  151.  
  152.         fGoodChannel = FALSE;
  153.  
  154.         if ( noErr == ( err = NewSpeechChannel( &aVSpec, &theChannel ) ) )
  155.         {
  156.             fGoodChannel = TRUE;
  157.             ftheChannel = theChannel;
  158.             
  159.             if ( cVoiceMenuID > 0 )// Get the item number in the Voice menu
  160.             {
  161.                 fCurrVoiceItem = FindVoiceItemNum( theVoice );
  162.             }
  163.  
  164.         }
  165.  
  166.     }
  167.  
  168.     return err;
  169.  
  170. }
  171.  
  172. /******************************************************************************
  173.  SpeakTheText
  174.  
  175.     Speak this text.  The input text is assumed to be in text format (not 
  176.     phoneme format) and is sent to the speech manager to be spoken.  
  177.     This method is used for speaking most text.  Note that text with 
  178.     embedded speech commands should use this function.
  179.  
  180. ******************************************************************************/
  181.  
  182. OSErr 
  183. CVoice::SpeakTheText( const char *theText, long len )
  184. {
  185.     return SpeakIt( theText, len, kTextInput );
  186.     
  187. }
  188.  
  189. /******************************************************************************
  190.  SpeakThePhonemes
  191.  
  192.     Speak this text.  The text is interpreted as being in phonemic format.
  193.  
  194. ******************************************************************************/
  195.  
  196. OSErr 
  197. CVoice::SpeakThePhonemes( const char *theText, long len )
  198. {
  199.     return SpeakIt( theText, len, kPhonInput );
  200.     
  201. }
  202.  
  203. /******************************************************************************
  204.  SpeakTheText
  205.  
  206.     Speak this text.  A copy is made of the text to be spoken so it may
  207.     be disposed of or altered after this routine returns.  Protected method.
  208.  
  209. ******************************************************************************/
  210.  
  211. OSErr 
  212. CVoice::SpeakIt( const char *theText, long len, short inputType )
  213. {
  214.     long        textlen = len;
  215.     OSErr        err = kBadChanErr;
  216.     Ptr            tPtr;
  217.     
  218.     if ( fGoodChannel )
  219.     {
  220.         //Interupt any current speech so we can manipulate the text buffer
  221.         err = StopSpeech( ftheChannel );
  222.         SetInputFormat( inputType );
  223.  
  224.         //Adjust size of pointer to accomodate size of text if necessary
  225.         if ( textlen > fTextPtrSize )
  226.         {
  227.             //First try to increase size of pointer, likely to fail
  228.             SetPtrSize( fTextPtr, textlen );
  229.             if ( MemError() == noErr )
  230.             {
  231.                 fTextPtrSize = textlen;
  232.             }
  233.             else
  234.             {
  235.                 //Next try to get a new, larger ptr
  236.                 SetPtrSize( fTextPtr, 2 );//Free up some mem
  237.                 tPtr = NewPtr( textlen );
  238.                 if ( MemError() == noErr )
  239.                 {
  240.                     DisposePtr( fTextPtr );//Dispose of old Pointer
  241.                     fTextPtr = tPtr;
  242.                     fTextPtrSize = textlen;
  243.                 }
  244.                 else
  245.                 {    //If no joy then restore the ptrsize and truncate the text to be spoken
  246.                     SetPtrSize( fTextPtr, fTextPtrSize );
  247.                     textlen = fTextPtrSize;
  248.                 }
  249.             }
  250.         }
  251.  
  252.         BlockMoveData( theText, fTextPtr, textlen );
  253.  
  254.         err = SpeakText( ftheChannel, fTextPtr, textlen );
  255.     }
  256.  
  257.     return err;
  258.  
  259. }
  260.  
  261. /******************************************************************************
  262.  SetInputFormat
  263.  
  264.     Set the current input format to either TEXT or PHON.  protected method.
  265.     This method can be faked out if the input format is changed with
  266.     embedded speech commands.
  267.  
  268. ******************************************************************************/
  269.  
  270. void 
  271. CVoice::SetInputFormat( short inputFormat )
  272. {
  273.     long        theFormat;
  274.     
  275.     if ( fInputFormat != inputFormat )
  276.     {
  277.         if ( inputFormat == kPhonInput )
  278.             theFormat = 'PHON';
  279.         else
  280.             theFormat = 'TEXT';
  281.  
  282.         if ( noErr == SetSpeechInfo( ftheChannel, soInputMode, &theFormat ) )
  283.             fInputFormat = inputFormat;
  284.     }
  285.  
  286. }
  287.  
  288. /******************************************************************************
  289.  GetPhonemeInfo
  290.  
  291.     Return the handle containing the phoneme info.  Phoneme info is a handle
  292.     containing ascii text showing all the phonemes recognized by this voice.
  293.  
  294. ******************************************************************************/
  295.  
  296. Handle 
  297. CVoice::GetPhonemeInfo( void )
  298. {
  299.     Handle        PhonDesc = NULL;
  300.     
  301.     if ( fGoodChannel )
  302.         (void) GetSpeechInfo( ftheChannel, soPhonemeSymbols, &PhonDesc );
  303.     
  304.     return PhonDesc;
  305.  
  306. }
  307.  
  308. /******************************************************************************
  309.  GetDescription
  310.  
  311.     Get a voice description for the current speech channel.
  312.  
  313. ******************************************************************************/
  314.  
  315. OSErr 
  316. CVoice::GetDescription( VoiceDescription *theVDesc ) const
  317. {
  318.     VoiceSpec        aVSpec = ftheSpec;    //Make local version of VSpec
  319.         
  320.     if ( fGoodChannel )
  321.         return GetVoiceDescription( &aVSpec, theVDesc, sizeof( VoiceDescription ) );
  322.     else
  323.         return kBadChanErr;
  324.  
  325. }
  326.  
  327. /******************************************************************************
  328.  GetVSpec
  329.  
  330.     Return the Voice Spec record for the current speech channel.
  331.  
  332. ******************************************************************************/
  333.  
  334. void 
  335. CVoice::GetVSpec( VoiceSpec *theVSpec ) const
  336. {
  337.     *theVSpec = ftheSpec;
  338.  
  339. }
  340.  
  341. /******************************************************************************
  342.  WordToPhoneme
  343.  
  344.     Return the phonemic representation of theWord in thePhoneme.  Both are
  345.     PStrings.  theWord can be any word or phrase.  thePhoneme will recieve
  346.     the phonemic representation of this word or phrase.  Remember that
  347.     phonemes are often two characters so the phonemic representation of a
  348.     long phrase may not fit in the 255 bytes of thePhoneme.  This routine
  349.     truncates the result and returns -2 if it's too long and has been 
  350.     truncated.
  351.     
  352. ******************************************************************************/
  353.  
  354. OSErr 
  355. CVoice::WordToPhoneme( const Str255 theWord, Str255 thePhoneme )
  356. {
  357.     long        phonelen = 0;
  358.     OSErr        err = kBadChanErr;
  359.     Handle        phonemehand = NewHandle( 256 );
  360.  
  361.     if ( !MemError() && fGoodChannel )
  362.     {
  363.         SetInputFormat( kTextInput );    //Needs to be text format
  364.         err = TextToPhonemes( ftheChannel, (Ptr) &theWord[1], theWord[0],
  365.                 phonemehand, &phonelen );
  366.  
  367.         if ( noErr == err)
  368.         {
  369.             if ( phonelen > 255 )
  370.             {
  371.                 phonelen = 255;
  372.                 err = -2;
  373.             }
  374.  
  375.             BlockMoveData( *phonemehand, &thePhoneme[1], phonelen );
  376.             thePhoneme[0] = phonelen;
  377.  
  378.         }
  379.     }
  380.     
  381.     ForgetHandle( phonemehand );
  382.     
  383.     return err;
  384.  
  385. }
  386.  
  387. /******************************************************************************
  388. AreWeSpeaking
  389.  
  390.      Return TRUE if the channel is busy.  If you wish to speak text without
  391.      interrupting currently speaking text, poll this method until it returns
  392.      FALSE.
  393.      
  394. ******************************************************************************/
  395.  
  396. Boolean 
  397. CVoice::AreWeSpeaking(void)
  398. {
  399.     SpeechStatusInfo    SpeechInfo;
  400.     long                result = FALSE;    //Default result
  401.     OSErr                err;
  402.  
  403.     if ( fGoodChannel )
  404.     {
  405.         err = GetSpeechInfo( ftheChannel, soStatus, &SpeechInfo );
  406.         
  407.         if ( err == noErr && SpeechInfo.outputBusy )
  408.              result = TRUE;
  409.      }
  410.  
  411.     return        result;
  412.  
  413. }
  414.  
  415. /******************************************************************************
  416. AreAnySpeaking
  417.  
  418.      Return TRUE if any channels are busy.
  419.      
  420. ******************************************************************************/
  421.  
  422. Boolean 
  423. CVoice::AreAnySpeaking(void)
  424. {
  425.     long                result = FALSE;    //Default result
  426.     
  427.     if ( cHasSpeechMan == kHasSpeechMan )
  428.     {
  429.         if ( SpeechBusy() > 0 )
  430.              result = TRUE;
  431.     }
  432.  
  433.     return        result;
  434.  
  435. }
  436.  
  437. /******************************************************************************
  438. AreWePaused
  439.  
  440.      Return TRUE if the channel is paused.
  441.      
  442. ******************************************************************************/
  443.  
  444. Boolean 
  445. CVoice::AreWePaused(void)
  446. {
  447.     SpeechStatusInfo    SpeechInfo;
  448.     long                result = FALSE;    //Default result
  449.     OSErr                err;
  450.  
  451.     if ( fGoodChannel )
  452.     {
  453.         err = GetSpeechInfo( ftheChannel, soStatus, &SpeechInfo );
  454.         
  455.         if ( err == noErr && SpeechInfo.outputPaused )
  456.              result = TRUE;
  457.      }
  458.  
  459.     return        result;
  460.  
  461. }
  462.  
  463. /******************************************************************************
  464. StopSpeaking
  465.  
  466.      Stop speaking for this voice.  whentostop is one of the constants defined
  467.      in Speech.h: kImmediate, kEndOfWord, or kEndOfSentence.  It is safe
  468.      to call this method on an already idle channel.
  469.      
  470. ******************************************************************************/
  471.  
  472. OSErr 
  473. CVoice::StopSpeaking( long whentostop )
  474. {
  475.     if ( fGoodChannel )
  476.         return StopSpeechAt( ftheChannel, whentostop );
  477.     else
  478.         return kBadChanErr;
  479.  
  480. }
  481.  
  482. /******************************************************************************
  483.  PauseSpeaking
  484.  
  485.      Pause speaking for this voice.  whentostop is one of the constants defined
  486.      in Speech.h: kImmediate, kEndOfWord, or kEndOfSentence.
  487.      
  488. ******************************************************************************/
  489.  
  490. OSErr 
  491. CVoice::PauseSpeaking( long whentostop )
  492. {
  493.     if ( fGoodChannel )
  494.         return PauseSpeechAt( ftheChannel, whentostop );
  495.     else
  496.         return kBadChanErr;
  497.  
  498. }
  499.  
  500. /******************************************************************************
  501. ContinueSpeaking
  502.  
  503.      Start speaking after having been paused.
  504.      
  505. ******************************************************************************/
  506.  
  507. OSErr 
  508. CVoice::ContinueSpeaking(void)
  509. {
  510.     if ( fGoodChannel )
  511.         return ContinueSpeech( ftheChannel );
  512.     else
  513.         return kBadChanErr;
  514.  
  515. }
  516.  
  517. /******************************************************************************
  518. NumVoices
  519.  
  520.      Return the number of voices installed on this machine.  Returns -1 if
  521.      no Speech Man.
  522.      
  523. ******************************************************************************/
  524.  
  525. short 
  526. CVoice::NumVoices(void)
  527. {
  528.     short        NumofVoices = -1;
  529.  
  530.     if ( cHasSpeechMan == kHasSpeechMan )
  531.         (void) CountVoices( &NumofVoices );
  532.         
  533.     return        NumofVoices;
  534.  
  535. }
  536.  
  537. /******************************************************************************
  538. GetVoicebyIndex
  539.  
  540.      Return the name of the nth voice installed.  Good values are 
  541.      1 to NumVoices().  This routine can be used to build a list of all
  542.      the names installed on a machine.  The returned name is a PString and
  543.      if any errors occurr the string will be empty.
  544.      
  545. ******************************************************************************/
  546.  
  547. OSErr 
  548. CVoice::GetVoicebyIndex( long index, Str63 theName )
  549. {
  550.     VoiceSpec            theVSpec;
  551.     VoiceDescription    VD;
  552.     OSErr                err = kNoSpManErr;
  553.  
  554.     theName[0] = 0;        //In case of errors
  555.  
  556.     if ( cHasSpeechMan == kHasSpeechMan )
  557.     {
  558.         err = GetIndVoice( index, &theVSpec );
  559.         if ( err == noErr )
  560.             err = GetVoiceDescription( &theVSpec, &VD, sizeof( VD ) );
  561.         if ( err == noErr )
  562.             BlockMoveData( VD.name, theName, VD.name[0]  + 1 );
  563.     }
  564.     
  565.     return err;
  566.  
  567. }
  568.  
  569. /******************************************************************************
  570. UseADictionary
  571.  
  572.     Add a dict resource to this speech channel.  The resource may be disposed
  573.     after calling this method.
  574.      
  575. ******************************************************************************/
  576.  
  577. OSErr 
  578. CVoice::UseADictionary( Handle thedict )
  579. {
  580.     OSErr                err = kBadChanErr;
  581.  
  582.     if ( fGoodChannel )
  583.         err = UseDictionary( ftheChannel, thedict );
  584.  
  585.     return err;
  586.  
  587. }
  588.  
  589. /******************************************************************************
  590. BuildVoiceMenu
  591.  
  592.     Build a menu that contains all the currently installed voices.  The menu
  593.     must already exist.  This method would usually be called only once from
  594.     the Application's SetUpMenus method.
  595.      
  596. ******************************************************************************/
  597.  
  598. OSErr 
  599. CVoice::BuildVoiceMenu( short theMenuID )
  600. {
  601.     OSErr            err = kNoSpManErr;
  602.     short            theNum;
  603.     long            i;
  604.     MenuHandle        VMenu;
  605.     Str63            theName;
  606.  
  607.     if ( cHasSpeechMan == kHasSpeechMan )
  608.     {
  609.         VMenu = gBartender->FindMacMenu( theMenuID );
  610.         ASSERT( VMenu );                                // Does the menu exist?
  611.         gBartender->SetDimOption( theMenuID, dimNONE );    // Don't dim every time
  612.         gBartender->SetUnchecking( theMenuID, TRUE );    // Do uncheck every time
  613.     
  614.         CountVoices( &theNum );                    // How many voices?
  615.         for ( i = 1; i <= theNum; i++ )
  616.         {
  617.             err = GetVoicebyIndex( i, theName );// Get a voice
  618.             if ( err == noErr )
  619.                 AppendMenu( VMenu, theName );    // Add the name to the menu
  620.         }
  621.     
  622.         cVoiceMenuID = theMenuID;                // Save the menu ID in the class variable
  623.     
  624.     }
  625.     
  626.     return err;
  627.  
  628. }
  629.  
  630. /******************************************************************************
  631. SetVoiceFromMenu
  632.  
  633.     Set the voice for this object.  Use this method when a Voice Menu was
  634.     created with BuildVoiceMenu.  It is called from the DoCommand method
  635.     of any Pane or Document that can have a voice associated with it.  Call
  636.     it like this:
  637.     
  638.     default:
  639.         if ( theCommand < 0 )
  640.             itsVoice->SetVoiceFromMenu( theCommand );
  641.         else
  642.             inherited::DoCommand( theCommand );
  643.         break;
  644.      
  645. ******************************************************************************/
  646.  
  647. OSErr 
  648. CVoice::SetVoiceFromMenu( long theCommand )
  649. {
  650.     short            menuID;
  651.     short            menuItem;
  652.     MenuHandle        VMenu;
  653.     OSErr            err;
  654.     short            saveMenuID;
  655.     Str63            VName;
  656.  
  657.     //Extract the menu ID and the Line number in the menu
  658.     theCommand = -theCommand;
  659.     menuID = HiShort( theCommand );
  660.     menuItem = LoShort( theCommand );
  661.     VMenu = gBartender->FindMacMenu( menuID );
  662.  
  663.     GetItem(  VMenu, menuItem, VName );    //Get the name of the voice
  664.  
  665.     //Set cVoiceMenuID to an invalid value so SetVoice won't bother 
  666.     //trying to finding the itemnum
  667.     saveMenuID = cVoiceMenuID;
  668.     cVoiceMenuID = -1;
  669.     
  670.     err = SetVoice( VName );    //Change the voice to the voice chosen
  671.  
  672.     cVoiceMenuID = saveMenuID;    //Reset the menuID to the saved value
  673.  
  674.     fCurrVoiceItem = menuItem;    //Save the line number in the menu
  675.     
  676.     return err;
  677.     
  678. }
  679.  
  680. /******************************************************************************
  681. UpdateMenus
  682.  
  683.      Place a checkmark in front of the current voice in the Voice menu.  
  684.      Call this from the UpdateMenus method of any pane or document that has 
  685.      a voice associated with it if a voice menu has been created by BuildVoiceMenu.
  686.  
  687. ******************************************************************************/
  688.  
  689. void 
  690. CVoice::UpdateMenus(void)
  691. {
  692.     MenuHandle        VMenu;
  693.  
  694.     if ( cVoiceMenuID > 0 )
  695.     {
  696.         VMenu = gBartender->FindMacMenu( cVoiceMenuID );
  697.         CheckItem( VMenu, fCurrVoiceItem, TRUE );
  698.  
  699.     }
  700.  
  701. }
  702.  
  703. /******************************************************************************
  704. FindVoiceItemNum
  705.  
  706.      Find the item number that matches the voice name in theVoice. 
  707.      Called by SetVoice when it doesn't know the line number in the voices 
  708.      menu that corresponds to a particular voice.  protected method.  
  709.  
  710. ******************************************************************************/
  711.  
  712. short 
  713. CVoice::FindVoiceItemNum( const Str63 theVoice )
  714. {
  715.     MenuHandle        VMenu;
  716.     short            numitems;
  717.     short            result = 0;
  718.     Str63            VName;
  719.  
  720.     VMenu = gBartender->FindMacMenu( cVoiceMenuID );
  721.  
  722.     ASSERT( VMenu );
  723.  
  724.     numitems = CountMItems( VMenu );
  725.  
  726.     for ( int i = 1; i <= numitems; i++ )
  727.     {
  728.         GetItem(  VMenu, i, VName );
  729.         if ( EqualString( theVoice, VName, TRUE, TRUE ) )
  730.         {
  731.             result = i;
  732.             break;
  733.         }
  734.     }
  735.     
  736.     return result;
  737.  
  738. }